GtkBuilder: Make IDs optional
authorMatthias Clasen <mclasen@redhat.com>
Mon, 18 Nov 2013 01:15:54 +0000 (20:15 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Mon, 18 Nov 2013 11:00:02 +0000 (06:00 -0500)
One requirement of .ui files is that each object must have an ID,
even if it is never referred to or directly loaded from the code.
This makes editing .ui files much more onerous than it has to be,
due to the frequent need to invent new IDs, while avoiding
clashes.

This commit makes IDs optional in the XML. They only need to
be provided for objects which are referred to or explictly loaded
from the code. Since GtkBuilder needs IDs for its own internal
accounting, we create IDs of the form ___object_N___ if not
specified in the XML.

https://bugzilla.gnome.org/show_bug.cgi?id=712553

gtk/gtkbuilder.c
gtk/gtkbuilder.rnc
gtk/gtkbuilder.rng
gtk/gtkbuilderparser.c
gtk/gtkbuilderprivate.h
testsuite/gtk/builder.c

index c6b6b2577a9237584a0b635d5cb977664fa26cdd..695b53e79f5219055a26ce5225aafd286232d9bd 100644 (file)
  * specifying the id of the #GtkUIManager in the "constructor" attribute and the
  * name of the object in the "id" attribute.
  *
- * Objects must be given a name with the "id" attribute, which allows the
+ * Objects may be given a name with the "id" attribute, which allows the
  * application to retrieve them from the builder with gtk_builder_get_object().
  * An id is also necessary to use the object as property value in other parts of
- * the UI definition.
+ * the UI definition. GTK+ reserves ids starting and ending with ___ (3 underscores)
+ * for its own purposes.
  * </para>
- * <note><para>
- * Prior to 2.20, GtkBuilder was setting the "name" property of constructed widgets to the
- * "id" attribute. In GTK+ 2.20 or newer, you have to use gtk_buildable_get_name() instead
- * of gtk_widget_get_name() to obtain the "id", or set the "name" property in your UI
- * definition.
- * </para></note>
  * <para>
  * Setting properties of objects is pretty straightforward with the
  * &lt;property&gt; element: the "name" attribute specifies the name of the
index 635fd4f2892c96b540c889d3c4ff7db4681ddb07..6e3aea3469a9094ed78bc552a9c2c21056b1efea 100644 (file)
@@ -9,7 +9,7 @@ requires = element requires {
 }
 
 object = element object {
-  attribute id { xsd:ID },
+  attribute id { xsd:ID } ?,
   attribute class { text },
   attribute type-func { text } ?,
   attribute constructor { text } ?,
index eee698a60749157176b4824075469dc05a62a122..341d19f3eaf08dd7486a5dfab4d07015b40e2786 100644 (file)
   </define>
   <define name="object">
     <element name="object">
-      <attribute name="id">
-        <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-      </attribute>
+      <optional>
+        <attribute name="id">
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+        </attribute>
+      </optional>
       <attribute name="class">
         <text/>
       </attribute>
index 11e04cb50690e6ad8323fa0d62443c968e8982e9..e2111d65c90b51533d9eec15800d6f447f16c1e0 100644 (file)
@@ -301,7 +301,7 @@ is_requested_object (const gchar *object,
 
   for (l = data->requested_objects; l; l = l->next)
     {
-      if (strcmp (l->data, object) == 0)
+      if (g_strcmp0 (l->data, object) == 0)
         return TRUE;
     }
 
@@ -369,10 +369,11 @@ parse_object (GMarkupParseContext  *context,
       return;
     }
 
+  data->object_counter++;
+
   if (!object_id)
     {
-      error_missing_attribute (data, element_name, "id", error);
-      return;
+      object_id = g_strdup_printf ("___object_%d___", data->object_counter++);
     }
 
   ++data->cur_object_level;
@@ -420,7 +421,6 @@ parse_object (GMarkupParseContext  *context,
       return;
     }
 
-
   g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line));
 }
 
index 346459c678d75bd189317943474d66224ec2af9c..b653373be79b553ba58da3d82885718fd713c274 100644 (file)
@@ -107,6 +107,8 @@ typedef struct {
   gint requested_object_level;
   gint cur_object_level;
 
+  gint object_counter;
+
   GHashTable *object_ids;
 } ParserData;
 
index 08582623c5cdd9d7b8c860fc0931d9ad35431bcb..d20fba6a61c62de925d6612f827ed0c14b27c2b8 100644 (file)
@@ -2728,6 +2728,53 @@ test_expose_object (void)
   g_object_unref (image);
 }
 
+static void
+test_no_ids (void)
+{
+  GtkBuilder *builder;
+  GError *error = NULL;
+  GObject *obj;
+  const gchar buffer[] =
+    "<interface>"
+    "  <object class=\"GtkInfoBar\">"
+    "    <child internal-child=\"content_area\">"
+    "      <object class=\"GtkHBox\">"
+    "        <child>"
+    "          <object class=\"GtkLabel\">"
+    "            <property name=\"label\" translatable=\"yes\">Message</property>"
+    "          </object>"
+    "          <packing>"
+    "            <property name='expand'>False</property>"
+    "          </packing>"
+    "        </child>"
+    "      </object>"
+    "    </child>"
+    "    <child internal-child=\"action_area\">"
+    "      <object class=\"GtkVButtonBox\">"
+    "        <child>"
+    "          <object class=\"GtkButton\" id=\"button_ok\">"
+    "            <property name=\"label\">gtk-ok</property>"
+    "            <property name=\"use-stock\">yes</property>"
+    "          </object>"
+    "        </child>"
+    "      </object>"
+    "    </child>"
+    "    <action-widgets>"
+    "      <action-widget response=\"1\">button_ok</action-widget>"
+    "    </action-widgets>"
+    "  </object>"
+    "</interface>";
+
+  builder = gtk_builder_new ();
+  gtk_builder_add_from_string (builder, buffer, -1, &error);
+  g_assert (error == NULL);
+
+  obj = gtk_builder_get_object (builder, "button_ok");
+  g_assert (GTK_IS_BUTTON (obj));
+
+  g_object_unref (builder);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -2777,6 +2824,7 @@ main (int argc, char **argv)
   g_test_add_func ("/Builder/GMenu", test_gmenu);
   g_test_add_func ("/Builder/LevelBar", test_level_bar);
   g_test_add_func ("/Builder/Expose Object", test_expose_object);
+  g_test_add_func ("/Builder/No IDs", test_no_ids);
 
   return g_test_run();
 }